﻿namespace HuwmanCode.IO

open System
open System.Security
open System.Security.Permissions
open System.Transactions
open System.Runtime.InteropServices
open Microsoft.Win32.SafeHandles
open System.Runtime.ConstrainedExecution

[<AutoOpen>]
module internal NativeMethods =
    [<Flags>]
    type internal NativeFileShare =
    | None = 0x00
    | Read = 0x01
    | Write = 0x02
    | Delete = 0x04

    [<Flags>]
    type internal NativeFileMode =
    | CreateNew = 1
    | CreateAlways = 2
    | CreateExisting = 3
    | OpenAlways = 4
    | TruncateExisting = 5

    [<Flags>]
    type internal NativeFileAccess =
    | GenericRead = 0x80000000
    | GenericWrite = 0x40000000

    [<DllImport("kernel32")>]  
    extern bool CloseHandle(nativeint handle)

    /// <summary>
    /// The KTM Transaction Handle
    /// </summary>
    [<SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)>]
    [<SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)>]
    type internal KtmTransactionHandle(handle) as this =
        inherit SafeHandleZeroOrMinusOneIsInvalid(true)

        do this.SetHandle(handle)

        [<ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)>]
        override sh.ReleaseHandle() =
            CloseHandle(sh.handle)

        /// <summary>
        /// Create a KTM Transaction Handle from a managed transaction
        /// </summary>
        static member Create(transaction) =
            let kernelTran = TransactionInterop.GetDtcTransaction(transaction) :?> IKernelTransaction
            let handle = kernelTran.GetHandle()
            new KtmTransactionHandle(handle)

        /// <summary>
        /// Create a KTM Transaction Handle from the current ambient transaction
        /// </summary>
        static member Create() =
            if Transaction.Current = null then raise(InvalidOperationException("There is no ambient transaction!"))
            else KtmTransactionHandle.Create(Transaction.Current)

    /// <summary>
    /// http://msdn.microsoft.com/en-us/library/aa344210(VS.85).aspx
    /// </summary>
    and [<ComImport>]
        [<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>]
        [<Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")>]
        internal IKernelTransaction =
            abstract GetHandle: [<Out>]handle:nativeint byref -> unit        

    /// <summary>
    /// http://msdn.microsoft.com/en-us/library/aa363916(VS.85).aspx
    /// </summary>
    [<DllImport("kernel32", SetLastError=true, EntryPoint="DeleteFileTransactedW")>]  
    extern bool DeleteFileTransacted([<MarshalAs(UnmanagedType.LPWStr)>] string fileName, KtmTransactionHandle transaction)

    // <summary>
    /// http://msdn.microsoft.com/en-us/library/aa363859(VS.85).aspx
    /// </summary>
    [<DllImport("kernel32", SetLastError=true, EntryPoint="CreateFileTransactedW")>]
    extern SafeFileHandle CreateFileTransacted([<MarshalAs(UnmanagedType.LPWStr)>] string fileName, NativeFileAccess desiredAccess,
        NativeFileShare shareMode, nativeint securityAttributes, NativeFileMode creationDisposition, int dwFlagsAndAttributes, nativeint hTemplateFile,
        KtmTransactionHandle transactionHandle, nativeint pusMiniVersion, nativeint extendedParameter);